[NOIP2019]括号树

2019-11-21

题意

在一棵以$1$为根的树中,每个节点都有一个左括号或者右括号

令$F(x)$为从$1$到$x$的路径上组成的括号串中合法的连续子串的数量

求$ans=\oplus_{x=1}^n x\cdot F(x)$

题解

先考虑对于一个字符串如何计算,令f为以当前位为结尾的合法串个数,整个串的总数即为f的求和

dfs顺便用栈维护即可

调试记录

前缀和要在进入下一层前处理好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <cstdio>
#include <stack>
#define LL long long
const int maxn = 5e5 + 5;
using namespace std;
struct E{
int to, nxt;
}e[maxn];
int head[maxn], tot = 0;
void addedge(int u, int v){
e[++tot].to = v, e[tot].nxt = head[u];
head[u] = tot;
}
char str[maxn];
int fa[maxn], f[maxn], sum[maxn];
stack <int> s; LL ans = 0;
void dfs(int cur){
if (str[cur] == '(') s.push(cur);
int tmp = -1;
if (str[cur] == ')'){
if (!s.empty()){
f[cur] = f[fa[s.top()]] + 1;
tmp = s.top();
s.pop();
}
} sum[cur] = f[cur] + sum[fa[cur]];
for (int i = head[cur]; i; i = e[i].nxt){
int v = e[i].to;
dfs(v);
}
if (str[cur] == '(' && !s.empty()) s.pop();
else if (tmp != -1) s.push(tmp);
ans ^= (1ll * cur * sum[cur]);
}
int n;
int main(){
scanf("%d%s", &n, str + 1);
for (int i = 2; i <= n; i++){
scanf("%d", fa + i);
addedge(fa[i], i);
}
dfs(1);
printf("%lld\n", ans);
return 0;
}